import 'dart:convert';
import 'dart:typed_data';
import 'package:binary/binary.dart';
import 'package:flutter/widgets.dart';

class ByteUtils {
  static String bytes2String(Uint8List list,
      [bool preventNonAsciiChar = true]) {
    int firstInvisibleAsciiChar = -1;
    for (int i = 0; i < list.length; i++) {
      if (list[i] < 0x20 || list[i] > 0x7e) {
        firstInvisibleAsciiChar = i;
        break;
      }
    }
    if (firstInvisibleAsciiChar > 0) {
      list = list.sublist(0, firstInvisibleAsciiChar);
    }
    try {
      return const Utf8Codec().decode(list);
    } catch (e) {
      debugPrint("bytes2String error: $e");
      return "Error";
    }
  }

  static List<String> bytes2StringList(Uint8List list) {
    List<String> strings = [];
    int lastInvalidByteIndex = 0;
    for (int i = 0; i < list.length; i++) {
      if (list[i] == 0 && i - 1 > lastInvalidByteIndex) {
        if (list[lastInvalidByteIndex] == 0) {
          lastInvalidByteIndex = i + 1;
          continue;
        }
        strings.add(
            const Utf8Codec().decode(list.sublist(lastInvalidByteIndex, i)));
        lastInvalidByteIndex = i + 1;
      }
    }
    return strings;
  }

  static String bytes2HexString(Uint8List list) {
    StringBuffer buffer = StringBuffer();
    for (int i in list) {
      String s = (i & 0xFF).toRadixString(16);
      s = s.length == 1 ? '0$s' : s;
      buffer.write(s);
    }
    return buffer.toString();
  }

  static String bytes2HexStringLe(Uint8List list,
      [int offset = 0, int length = 0]) {
    if (length == 0) length = list.length;
    StringBuffer buffer = StringBuffer();
    for (int i = 0; i < length; i++) {
      String s = (list[length - i - offset - 1] & 0xFF).toRadixString(16);
      s = s.length == 1 ? '0$s' : s;
      buffer.write(s);
    }
    return buffer.toString();
  }

  static String numberLe2HexString(int num) {
    int numberLength = 1;
    if (num <= 0xFFFF) {
      numberLength = 2;
    } else if (num <= 0xFFFFFF) {
      numberLength = 3;
    } else {
      numberLength = 4;
    }
    return bytes2HexString(num2BytesLe(num, numberLength));
  }

  static Uint8List hexString2Bytes(String hexString) {
    int len = hexString.length;
    Uint8List bytes = Uint8List(len ~/ 2);
    for (int i = 0; i < len; i += 2) {
      bytes[i ~/ 2] = hexStr2Byte(hexString.substring(i, i + 2)).value;
    }
    return bytes;
  }

  static void copy(
      Uint8List src, int srcIndex, Uint8List dest, int destIndex, int length) {
    for (int i = 0; i < length; i++) {
      dest[destIndex + i] = src[srcIndex + i];
    }
  }

  static Uint8List num2Bytes(int num, int length,
      [Uint8List? dest, int offset = 0]) {
    if (num < 0) num = ~num + 1;
    dest ??= Uint8List(length);
    for (int i = 0; i < length; i++) {
      dest[length + offset - 1 - i] = (num & 0xFF);
      num >>= 8; // 向右移8位
    }
    return dest;
  }

  static Uint8List num2BytesLe(int num, int length,
      [Uint8List? dest, int offset = 0]) {
    dest ??= Uint8List(length);
    for (int i = 0; i < length; i++) {
      dest[i + offset] = (num & 0xFF);
      num >>= 8; // 向右移8位
    }
    return dest;
  }

  static Uint8List str2Bytes(String str, [bool autoAdd0 = false]) {
    List<int> bytes = utf8.encode(str);
    if (autoAdd0) {
      return buildBytes(Uint8List.fromList(bytes), Uint8List.fromList([0]));
    }
    return Uint8List.fromList(bytes);
  }

  static Uint8 hexStr2Byte(String str, [int offset = 0, int length = 2]) {
    return toUint8(
        int.parse(str.substring(offset, offset + length), radix: 16));
  }

  static int byte2UInt(int i) {
    return toUint8(i & 0xFF).value;
  }

  static String byte2Hex(int i) {
    i = i & 0xFF;
    return i < 0x10 ? "0${i.toRadixString(16)}" : i.toRadixString(16);
  }

  static int bytes2IntLe(Uint8List bytes, [int src = 0, int length = 1]) {
    switch (length) {
      case 1:
        return bytes[src];
      case 2:
        var v = (bytes[src + 1] << 8) | bytes[src];
        return Int16(v > 0x7FFF ? -(0x10000 - v) : v).value;
      case 4:
        int v = (bytes[src + 3] << 24) |
            (bytes[src + 2] << 16) |
            (bytes[src + 1] << 8) |
            bytes[src];
        return Int32(v > 0x7FFFFFFF ? (-(~v & 0xFFFFFFFF) + 1) : v).value;
    }
    throw Exception('bytes2IntLe; Unsupported integer length $length');
  }

  static int bytes2UIntLe(Uint8List bytes, [int src = 0, int length = 1]) {
    switch (length) {
      case 1:
        return toUint8(bytes[src] & 0xFF).value;
      case 2:
        return toUint16(((bytes[src + 1] << 8) | bytes[src]) & 0xFFFF).value;
      case 4:
        return toUint32(((bytes[src + 3] << 24) |
                    (bytes[src + 2] << 16) |
                    (bytes[src + 1] << 8) |
                    bytes[src]) &
                0xFFFFFFFF)
            .value;
    }
    throw Exception('bytes2IntLe; Unsupported integer length $length');
  }

  static Int8 bytes2Int8Le(Uint8List bytes, [int src = 0]) {
    int v = bytes[src] & 0xFF;
    if (v > 0x7F) v = 0x7F - v;
    return Int8(v);
  }

  static Uint4 bytes2Uint4Le(Uint8List bytes, [int src = 0]) {
    return Uint4(bytes[src] & 0xFF);
  }

  static Uint8 bytes2Uint8Le(Uint8List bytes, [int src = 0]) {
    return Uint8(bytes[src] & 0xFF);
  }

  static Uint16 bytes2Uint16Le(Uint8List bytes, [int src = 0]) {
    return Uint16(((bytes[src + 1] << 8) | bytes[src]) & 0xFFFF);
  }

  static Int16 bytes2Int16Le(Uint8List bytes, [int src = 0]) {
    int v = ((bytes[src + 1] << 8) | bytes[src]) & 0xFFFF;
    if (v > 0x7FFF) v = v - 0x10000;
    return Int16(v);
  }

  static int bytes2Uint64Le(Uint8List bytes, [int src = 0]) {
    return ((bytes[src + 7] << 56) |
            (bytes[src + 6] << 48) |
            (bytes[src + 5] << 40) |
            (bytes[src + 4] << 32) |
            (bytes[src + 3] << 24) |
            (bytes[src + 2] << 16) |
            (bytes[src + 1] << 8) |
            bytes[src]) &
        0xFFFFFFFFFFFFFFFF;
  }

  static Uint32 bytes2Uint32Le(Uint8List bytes, [int src = 0]) {
    return Uint32(((bytes[src + 3] << 24) |
            (bytes[src + 2] << 16) |
            (bytes[src + 1] << 8) |
            bytes[src]) &
        0xFFFFFFFF);
  }

  static Uint32 bytes2Uint24(Uint8List bytes, [int src = 0]) {
    return Uint32(
        ((bytes[src] << 16) | (bytes[src + 1] << 8) | bytes[src + 2]) &
            0xFFFFFFFF);
  }

  static Int32 bytes2Int32Le(Uint8List bytes, [int src = 0]) {
    int v = ((bytes[src + 3] << 24) |
            (bytes[src + 2] << 16) |
            (bytes[src + 1] << 8) |
            bytes[src]) &
        0xFFFFFFFF;
    debugPrint("bytes2Int32Le: $v");
    if (v > 0x7FFFFFFF) v = v - 0x100000000;
    debugPrint("bytes2Int32Le: $v");
    return Int32(v);
  }

  static void reverse(Uint8List src) {
    for (int i = 0, length = src.length ~/ 2; i < length; i++) {
      int t = src[i];
      src[i] = src[length - i - 1];
      src[length - i - 1] = t;
    }
  }

  static bool arrayEquals(Uint8List list1, Uint8List list2) {
    if (list1.length != list2.length) return false;
    for (int i = 0, length = list1.length; i < length; i++) {
      if (list1[i] != list2[i]) return false;
    }
    return true;
  }

  static Uint8List buildBytes(Uint8List p1,
      [Uint8List? p2, Uint8List? p3, Uint8List? p4]) {
    Uint8List result = Uint8List(p1.length +
        (p2 == null ? 0 : p2.length) +
        (p3 == null ? 0 : p3.length) +
        (p4 == null ? 0 : p4.length));
    result.setRange(0, p1.length, p1);
    if (p2 != null) result.setRange(p1.length, p1.length + p2.length, p2);
    if (p3 != null) {
      result.setRange(
          p1.length + p2!.length, p1.length + p2.length + p3.length, p3);
    }
    if (p4 != null) {
      result.setRange(p1.length + p2!.length + p3!.length,
          p1.length + p2.length + p3.length + p4.length, p4);
    }
    return result;
  }

  static Uint8List buildByte(int p1, [int? p2, int? p3, int? p4]) {
    Uint8List result = Uint8List(
        1 + (p2 == null ? 0 : 1) + (p3 == null ? 0 : 1) + (p4 == null ? 0 : 1));
    result[0] = p1;
    if (p2 != null) result[1] = p2;
    if (p3 != null) result[2] = p3;
    if (p4 != null) result[3] = p4;
    return result;
  }

  static Uint8List toInt8List(dynamic p, [int length = 0]) {
    if (p is String) return Uint8List.fromList(utf8.encode(p));
    if (p is int) return num2BytesLe(p, length);
    if (p is Uint8List) return p;
    throw Exception('Unknown type ${p.runtimeType}');
  }

  static bool byteEquals(int b1, int b2) {
    if (b1 < -127 || b1 > 255 || b2 < -127 || b2 > 255) return false;
    if (b1 < 0) b1 = 256 + b1;
    if (b2 < 0) b2 = 256 + b2;
    return b1 == b2;
  }

  static Int8 toInt8(int i) {
    i &= 0xFF;
    if (i >= 0x100 ~/ 2) i = i - 0x100;
    return Int8(i);
  }

  static int toInt8Int(int i) {
    i &= 0xFF;
    if (i >= 0x100 ~/ 2) i = i - 0x100;
    return i;
  }

  static Int16 toInt16(int i) {
    i &= 0xFFFF;
    if (i >= 0x10000 ~/ 2) i = i - 0x10000;
    return Int16(i);
  }

  static Int32 toInt32(int i) {
    i &= 0xFFFFFFFF;
    if (i >= 0x100000000 ~/ 2) i = i - 0x100000000;
    return Int32(i);
  }

  static Uint4 toUint4(int i) {
    i &= 0xFF;
    if (i <= -0x80 ~/ 2) i = 0x80 + i;
    return Uint4(i);
  }

  static Uint8 toUint8(int i) {
    i &= 0xFF;
    if (i <= -0x100 ~/ 2) i = 0x100 + i;
    return Uint8(i);
  }

  static Uint16 toUint16(int i) {
    i &= 0xFFFF;
    if (i <= -0x10000 ~/ 2) i = 0x10000 + i;
    return Uint16(i);
  }

  static Uint32 toUint32(int i) {
    i &= 0xFFFFFFFF;
    if (i <= -0x100000000 ~/ 2) i = 0x100000000 + i;
    return Uint32(i);
  }

  static Uint4 bitValue(int byte, int from, int length) {
    return Uint4(((byte << (8 - from - length)) & 0xFF) >> (8 - length));
  }

  static Uint4 bool2Uint4(bool b, [bool reverse = false]) {
    return Uint4((b || reverse) ? 1 : 0);
  }

  static Uint8 bool2Byte(bool b, [bool reverse = false]) {
    return Uint8((b || reverse) ? 1 : 0);
  }

  static Uint8List bool2Bytes(bool b, [bool reverse = false]) {
    Uint8List bytes = Uint8List(1);
    bytes[0] = bool2Byte(b, reverse).value;
    return bytes;
  }
}
